package libtool65.cmd;


import java.io.FileOutputStream;
import java.io.IOException;

import libtool65.link.Linker;
import libtool65.link.LinkerException;
import libtool65.o65.Module;
import libtool65.o65.Reader;
import libtool65.vin.RelocationList;

public class RelinkCommand extends LinkerBase {
	private static final byte[] VIN_HEADER_MAGIC = { 0x56, 0x49, 0x4e, 0x30 };
	Option def_option = new Option(true);
	Option out_option = new Option(true);

	@Override
	public Option addOption(String option_string) {
		if (option_string.equals("--def"))
			return def_option;
		if (option_string.equals("--out"))
			return out_option;
		return super.addOption(option_string);
	}

	@Override
	public void execute() throws Exception {
		Reader reader = new Reader();
		Linker linker = new Linker();
		for (String file : parameters)
		{
			Module module = readModule(reader, file);
			if (module != null)
				linker.add(file, module);
		}
		if (linker.hasUnresolvedReferences())
		{
			System.out.println("Unresolved references:");
			for (String unref : linker.getUnresolvedReferences())
			{
				System.out.println("  "+unref);
			}
		}
		else
		{
			String out_filename = out_option.getText();
			if (out_filename != null)
				writeOutput(out_filename, linker);
		}
	}

	private void writeOutput(String file_name, Linker linker) throws LinkerException {
		int code_size = linker.link(0, 0);
		RelocationList relocations = linker.getRelocationList();
		FileOutputStream outfile = null;
		try
		{
			outfile = new FileOutputStream(file_name);
			outfile.write(VIN_HEADER_MAGIC);
			int hdr_size = 3;
			int [] parts = new int [1];
			parts[0] = 12 + code_size + relocations.getByteSize();
			writeVinHeader(outfile, hdr_size, parts);
			writeCodeHeader(outfile, linker.getTotalSize(), linker.getZPSize(), code_size, relocations.getByteSize());
			linker.writeBinary(outfile, false);
			writeWord(outfile, relocations.getCount());
			relocations.write(outfile);
			writeWord(outfile, linker.resolveReference("main"));
		}
		catch (IOException e)
		{
			System.out.println("File write failed for \""+file_name+"\": "+e.getMessage());
		}
		finally
		{
			if (outfile != null)
				try { outfile.close(); } catch (IOException e) {}
		}
		
	}

	protected void writeVinHeader(FileOutputStream outfile, int hdr_size,
			int[] parts) throws IOException {
		int data_size = 0;
		for (int part_size : parts)
			data_size += part_size;
		int file_size = VIN_HEADER_MAGIC.length + 1 + hdr_size + data_size;
		writeByte(outfile, hdr_size);
		writeWord(outfile, file_size);
		writeByte(outfile, parts.length);
	}
	
	protected void writeCodeHeader(FileOutputStream outfile, int total_size, int zp_size, int code_size, int reloc_size) throws IOException
	{
		writeWord(outfile, 12 + code_size + reloc_size);
		writeByte(outfile, 0);	// PROGRAM
		writeWord(outfile, total_size);
		writeByte(outfile, zp_size);
		writeWord(outfile, code_size);
	}

	protected void writeWord(FileOutputStream outfile, int data)
			throws IOException {
		writeByte(outfile, data);
		writeByte(outfile, data >> 8);
	}

	protected void writeByte(FileOutputStream outfile, int data)
			throws IOException {
		outfile.write(data & 0xff);
	}

	@Override
	public void printHelp() {
		System.out.println("Links modules for dynamic loading by VIN.");
		System.out.println("Parameters:");
		System.out.println("   --def <file name>   ... linker definition file name");
		System.out.println("   --out <file name>   ... output file name");
	}
}
